#include "server.h"

#define _MONSVC_FUNC(svc)  \
    static int _MONSVC_FUNC_##svc(char *body, char *msg_out)

_MONSVC_FUNC(HELP)
{/*{{{*/
    static struct
    {
        char *cmd; 
        char *desc;
        char *usage;
    } a_cmd_map[]=
    {
        {
            "printworker",
            "print connection quantity of worker",
            "printworker"
        },
        {
            "printsender",
            "print mail status of senders",
            "printsender"
        },
        { NULL, NULL, NULL }
    };
    int offset=0;
    int i;

    // 所有帮助
    if(body)
        while(*body == SPACE)
            body++;
    if(!body || BUF_IS_CLR(body))
    {
        offset+=sprintf(msg_out+offset, "help [command]\n");
        offset+=sprintf(msg_out+offset, "commands:\n");
        for(i=0; a_cmd_map[i].cmd; i++)
            offset+=sprintf(msg_out+offset, "    %s\n",
                    a_cmd_map[i].cmd);
        return(offset);
    }

    // 单独命令帮助
    for(i=0; a_cmd_map[i].cmd; i++)
        if(strcmp(a_cmd_map[i].cmd, body) == 0)
        {
            offset+=sprintf(msg_out+offset, "Description: %s\n",
                    a_cmd_map[i].desc);
            offset+=sprintf(msg_out+offset, "Usage:\n    %s\n",
                    a_cmd_map[i].usage);
            return(offset);
        }

    offset+=sprintf(msg_out+offset, "Command not found, type help\n");
    return(offset);
}/*}}}*/

_MONSVC_FUNC(PRINTWORKER)
{/*{{{*/
    int offset=0;
    int i;
    int conn_total_num=0;

    offset+=sprintf(msg_out+offset, "  worker:    clients\n");
    pthread_mutex_lock(&share_info->stat_lock);
    for(i=0; i<share_info->worker_num; i++)
    {
        offset+=sprintf(msg_out+offset, "  %6d      %6d\n",
                i, share_info->worker_stat[i].slot_used_num-1);
        conn_total_num+=share_info->worker_stat[i].slot_used_num-1;
    }
    pthread_mutex_unlock(&share_info->stat_lock);
    offset+=sprintf(msg_out+offset, "\n   total:     %6d\n",
            conn_total_num); 

    return(offset);
}/*}}}*/

_MONSVC_FUNC(PRINTSENDER)
{/*{{{*/
    int offset=0;
    int i;
    int send_total_num;
    int todo_total_num;

    offset+=sprintf(msg_out+offset, "  sender:        send        todo\n");
    pthread_mutex_lock(&share_info->stat_lock);
    send_total_num=todo_total_num=0;
    for(i=0; i<share_info->sender_num; i++)
    {
        if(i == SENDER_VIP_IDX)
            offset+=sprintf(msg_out+offset, "     vip");
        else
            offset+=sprintf(msg_out+offset, "   %5d", i);
        offset+=sprintf(msg_out+offset, "     %8d      %6d\n",
                share_info->sender_stat[i].send_num,
                share_info->sender_stat[i].todo_num);
        send_total_num+=share_info->sender_stat[i].send_num;
        todo_total_num+=share_info->sender_stat[i].todo_num;
    }
    pthread_mutex_unlock(&share_info->stat_lock);
    offset+=sprintf(msg_out+offset, "\n   total:    %8d      %6d\n",
            send_total_num, todo_total_num); 

    return(offset);
}/*}}}*/

static int _svc(struct monitor_ctl *monitor_ctl)
{/*{{{*/
typedef int (*_P_MONSVC_FUNC)(char *body, char *msg_out);
    static struct
    {
        char *svc;
        _P_MONSVC_FUNC p_func;
    } a_monsvc_map[]=
    {
        { "HELP", _MONSVC_FUNC_HELP },
        { "PRINTWORKER", _MONSVC_FUNC_PRINTWORKER },
        { "PRINTSENDER", _MONSVC_FUNC_PRINTSENDER },
        { NULL, NULL }
    };
    char *msg_in=monitor_ctl->msg_in;
    char *msg_out=monitor_ctl->msg_out;
    int offset=0;
    char *body;
    int svcsize;

    body=strchr(msg_in, SPACE);
    if(body)
    {
        svcsize=body-msg_in;
        while(*body == SPACE) body++;
    }
    else
        svcsize=strlen(msg_in);
    if(svcsize > 0 && svcsize <= NAME_SIZE)
    {
        char svc[NAME_SIZE+1];
        int i;

        strncpy(svc, msg_in, svcsize);
        svc[svcsize]=ZERO; 
#ifdef _MY_DEBUG
        errlog("%s: svc(%s)", __func__, svc);
#endif
        for(i=0; a_monsvc_map[i].svc; i++)
            if(strcasecmp(a_monsvc_map[i].svc, svc) == 0)
                break;
        if(a_monsvc_map[i].svc)
        {
            offset+=a_monsvc_map[i].p_func(body, msg_out);
        }
        else
        {
            offset+=sprintf(msg_out+offset, "command(%s) not found", svc);
            errlog("%s %s: %s", __FILE__, __func__, msg_out);
            offset+=sprintf(msg_out+offset, "\n");
        }
    }
    else
    {
        offset+=sprintf(msg_out+offset, "command invalid");
        errlog("%s %s: %s", __FILE__, __func__, msg_out);
        offset+=sprintf(msg_out+offset, "\n");
    }
    offset+=sprintf(msg_out+offset, TEXTEND);

    return offset;
}/*}}}*/

int thrd_monitor(struct monitor_ctl *monitor_ctl)
{/*{{{*/
#define _TIMEOUT        5
#define _TIMEOUT_MS     (_TIMEOUT*1000) 
    char *msg_in=monitor_ctl->msg_in;
    int msg_in_size;
    char *msg_out=monitor_ctl->msg_out;
    int msg_out_size;
    SKT term_skt;
    int res;
    int timeout_ms;
    int accu_timeout;

    while(1)
    {
        timeout_ms=_TIMEOUT_MS;
        if((res=skt_accept(monitor_ctl->skt, &timeout_ms, &term_skt)))
        {
            if(res != RET_ERR_TIMEOUT)
                errlog("%s %s: accept fail", __FILE__, __func__);
            continue;
        }
#ifdef _MY_DEBUG
        errlog("%s %s: console login", __FILE__, __func__);
#endif

        accu_timeout=0;
        while(1)
        {
            timeout_ms=_TIMEOUT_MS;
            if((res=skt_read(term_skt,
                            (unsigned char *)msg_in, &msg_in_size,
                            SKT_R_LINE, &timeout_ms)) < 0)
            {
                if(res == RET_ERR_TIMEOUT)
                {
                    accu_timeout+=_TIMEOUT;
                    if(accu_timeout < MONITOR_IDLE_TIMEOUT)
                        continue;
                    errlog("%s %s: console timeout, revoke",
                            __FILE__, __func__);
                }
                else
                {
                    if(res == RET_ERR_ABORT)
                        errlog("%s %s: console revoke",
                                __FILE__, __func__);
                    else
                        errlog("%s %s: read console fail, res:%d, revoke",
                                __FILE__, __func__, res);
                }
                skt_close(term_skt);
                break;
            }
            if(msg_in[msg_in_size-1] == RTN)
            {
                if(msg_in[msg_in_size-2] == '\r')
                    msg_in[msg_in_size-2]=ZERO;
                else
                    msg_in[msg_in_size-1]=ZERO;
            }
            else
                msg_in[msg_in_size]=ZERO;
            trim_head_space(msg_in);
            trim_tail_space(msg_in);
            accu_timeout=0;

            msg_out_size=_svc(monitor_ctl);

            timeout_ms=_TIMEOUT_MS;
            if((res=skt_write(term_skt,
                            (unsigned char *)msg_out, &msg_out_size,
                            SKT_W_SIZE, &timeout_ms)) < 0)
            {
                errlog("%s %s: respond to console fail, res:%d, revoke",
                        __FILE__, __func__, res);
                skt_close(term_skt);
                break;
            }
        } // console
    }

    return 0;
}/*}}}*/

#if 0
typedef int (*_P_CMD_FUNC)(struct monitor_ctl *, char *para);
#define _CMD_FUNC(_cmd)  \
static int _CMD_FUNC_##_cmd(struct monitor_ctl *monitor_ctl, char *para)

_CMD_FUNC(SHUTDOWN)
{/*{{{*/
    int rtn;

    rtn=-1;
    return(rtn);
}/*}}}*/

static int _cmd(struct monitor_ctl *monitor_ctl)
{/*{{{*/
    static struct
    {
        char *cmd;
        _P_CMD_FUNC p_cmd_func;
    } a_cmd_map[]=
    {
        { "SHUTDOWN", _CMD_FUNC_SHUTDOWN },
        { NULL, NULL }
    };
    char *para;
    char *cmd;
    char *msg_out;

    para=monitor_ctl->msg_in;
    if((cmd=para_get_item(monitor_ctl->msg_in, &para)))
    {
        int i;
        for(i=0; a_cmd_map[i].cmd; i++)
            if(strcmp(a_cmd_map[i].cmd, cmd) == 0)
                return(a_cmd_map[i].p_cmd_func(monitor_ctl, para));
    }
    errlog("%s: cmd(%s) not found", __func__, cmd?cmd:SPACES);
    msg_out=monitor_ctl->msg_out;
    CLR_BUF(msg_out);
    para_put_item(msg_out, SNMON_ERR_CMD_NOT_FOUND); 
    return(-1);
}/*}}}*/

int monitor(struct monitor_ctl *monitor_ctl)
{/*{{{*/
    char *msg_in=monitor_ctl->msg_in;
    int msg_in_size;
    char *msg_out=monitor_ctl->msg_out;
    int msg_out_size;
    SOCKET term_sock;
    int res;
    int rtn;
    int timeout;

    while(1)
    {
        if((term_sock=sock_accept_ex(monitor_ctl->monitor_sock,
                        IN_TIMEOUT)) < 0)
        {
            if(term_sock != SOCK_TIMEOUT)
                errlog("%s: accept conn fail", __func__);
            continue;
        }

        timeout=0;
        while(1)
        {
            if((res=sock_read_ex(term_sock, (BYTE *)msg_in,
                            &msg_in_size, IN_TIMEOUT, WRAP)) != 0)
            {
                if(res == RET_ERR_TIMEOUT)
                {
                    timeout+=IN_TIMEOUT;
                    if(timeout < MONITOR_IDLE_TIMEOUT)
                        continue;
                    errlog("%s: mon console timeout, revoke", __func__);
                }
                else
                {
                    if(res == RET_ERR_ABORT)
                        errlog("%s: mon console logout", __func__);
                    else
                        errlog("%s: read console fail, res:%d, revoke",
                                __func__, res);
                }
                sock_close(term_sock);
                break;
            }
            timeout=0;

            rtn=_cmd(monitor_ctl);

            msg_out_size=strlen(msg_out);
            if((res=sock_write_ex(term_sock, (BYTE *)msg_out,
                            &msg_out_size, IN_TIMEOUT, WRAP)) != 0)
            {
                errlog("%s: respond to console fail, res:%d，revoke",
                        __func__, res);
                sock_close(term_sock);
                break;
            }
            if(rtn == _SHUTDOWN_)
            {
                sleep(1);
                sock_close(term_sock);
                goto OUT_SHUTDOWN;
            }
        } // 与终端交互
    }

OUT_SHUTDOWN:
    return(0);
}/*}}}*/
#endif
